﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="StrategyPool.cs" company="">
//   
// </copyright>
// <summary>
//   The strategy pool.
// </summary>
// --------------------------------------------------------------------------------------------------------------------


using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using Systemathics.FrameWork.Interfaces;

namespace Systemathics.FrameWork
{
    public sealed class StrategyPool : IDisposable
    {
        public Dictionary<Guid, Strategy> StrategiesAssembly
        { get; private set; }
        public OrderService orderService
        { get; private set; }
        public DataService dataService
        { get; private set; }
        public IFees FeesProvider
        { get; private set; }
        public bool isConnected
        {
            get { return dataService.isConnected & orderService.isConnected; }
        }
        public VirtualMarketParameters VMP 
        { get; set;}
        
        public event EventHandler<FireMessageEventArgs> StrategyPoolOnConnection;
        public event EventHandler<FireMessageEventArgs> StrategyPoolEmitError;

        private readonly object mixedProvider;
        private readonly string orderProviderName = String.Empty;
        private bool disposed;

        public StrategyPool(string dataProviderName, string orderProviderName, string feesprovider)
        {
            StrategiesAssembly = new Dictionary<Guid, Strategy>();
            this.orderProviderName = orderProviderName;

            if(dataProviderName != orderProviderName)
            {
                var ds = ProviderFactory.GetDataProvider(dataProviderName, GlobalVariables.ASSEMBLY);
                if (ds != null)
                    dataService = new DataService(ds);
            }
            else
            {
                mixedProvider = ProviderFactory.GetMixedProvider(dataProviderName, GlobalVariables.ASSEMBLY);

                if (mixedProvider != null)
                    dataService = new DataService((IDataProvider)mixedProvider);    
            }
            FeesProvider = ProviderFactory.GetFeesProvider(feesprovider, GlobalVariables.ASSEMBLY);
        }

        public void Connect()
        {
            dataService.EmitError += EmitError;
            dataService.EmitConnectionStatus += DataProviderEmitConnectionStatus;

            dataService.Connect();
            // orderService connection in dataprovider callback
        }
        public void Disconnect()
        {
            orderService.Disconnect();
            dataService.Disconnect();

            dataService.EmitError -= EmitError;
            dataService.EmitConnectionStatus -= DataProviderEmitConnectionStatus;

            orderService.EmitError -= EmitError;
            orderService.EmitConnectionStatus -= OrderProviderEmitConnectionStatus;
        }
        public void BuildUp(DBStrategies Cell)
        {
            var directory = new DirectoryInfo(GlobalVariables.AUTOMATES);
            foreach (var file in directory.GetFiles("*.dll"))
            {
                var assembly = Assembly.Load(AssemblyName.GetAssemblyName(file.FullName));
                var Tipe = assembly.GetTypes()[0];
                if(Tipe.IsSubclassOf(typeof(Strategy)))
                {
                    var attributes = Attribute.GetCustomAttributes(Tipe, typeof(Pattern));
                    foreach (var attribute in attributes)
                    {
                        var p = (Pattern)attribute;
                        if (p != null)
                        {
                            if (p.PatternName == Cell.Pattern && p.Version == Cell.Version)
                            {
                                var strategy = assembly.CreateInstance(Tipe.ToString()) as Strategy;
                                if (strategy != null)
                                {
                                    strategy.dataService = dataService;
                                    strategy.orderService = orderService;
                                    strategy.feesService = FeesProvider;
                                    strategy.Cell = Cell;
                                    StrategiesAssembly.Add(Cell.ID, strategy);
                                    break;
                                }
                            }
                        }   
                    }
                }
            }
        }

        public bool ContainsStrategyKey(Guid key)
        {
            return StrategiesAssembly.ContainsKey(key);
        }
        public void Remove(Guid id)
        {
            StrategiesAssembly.Remove(id);
            using (var db = new SystemathicsEntities(GlobalVariables.EntitiesConnectionString()))
            {
                var todelete = db.DBStrategies.Where(y => y.ID == id);
                if (todelete.Any())
                {
                    db.DeleteObject(todelete.FirstOrDefault());
                    db.SaveChanges(true);
                }
            }
        }

        #region StrategyPool Call Backs

        private bool isConnecting;
        private void EmitError(object sender, FireMessageEventArgs e)
        {
            if (StrategyPoolEmitError != null)
                StrategyPoolEmitError(sender, e);
        }
        private void DataProviderEmitConnectionStatus(object sender, FireMessageEventArgs e)
        {
            // Initiate Order Provider once we already got a Data Provider Connection established. 
            // because if virtual is selected, Data provider must be running first
            if (StrategyPoolOnConnection != null)
            {
                switch ((ConnectionStatus) e.Message)
                {
                    case ConnectionStatus.Connected:
                        if (!isConnecting)
                        {
                            isConnecting = true;
                            if (mixedProvider != null)
                            {
                                orderService = new OrderService((IOrderProvider) mixedProvider);
                                // Force to emit connection status for mixed provider because, everything has already been connected (in data provider connection)
                                // because its in the same library
                                if (((IConnection) mixedProvider).isConnected)
                                    OrderProviderEmitConnectionStatus(this, new FireMessageEventArgs(ConnectionStatus.Connected, DateTime.Now.TimeOfDay));
                            }
                            else
                            {
                                // here, as Order Provider is in another library, connection event will come naturally from this library
                                if(orderProviderName == StrategyMode.Virtual.ToString() || orderProviderName == StrategyMode.BackTest.ToString())
                                {
                                    var IorderProvider = ProviderFactory.GetVirtualOrderProvider(dataService, GlobalVariables.ASSEMBLY);
                                    var type = IorderProvider.GetType();
                                    var method = type.GetMethod("SetVirtualMarketParameters");
                                    method.Invoke(IorderProvider, new object[] { VMP });
                                    orderService = new OrderService(IorderProvider);
                                }
                                else
                                    orderService = new OrderService(ProviderFactory.GetOrderProvider(orderProviderName, GlobalVariables.ASSEMBLY)) ;
                                    
                                orderService.EmitError += EmitError; // We don't wish to be notified twice time cause if mixedpro, dont need to suscribe twice emiterror
                            }

                            orderService.EmitConnectionStatus += OrderProviderEmitConnectionStatus;
                            orderService.Connect();
                        }

                        break;
                    case ConnectionStatus.Disconnected:
                        StrategyPoolOnConnection(sender, e);
                        break;
                    default:
                        break;
                }
            }
        }
        private void OrderProviderEmitConnectionStatus(object sender, FireMessageEventArgs e)
        {
            if (StrategyPoolOnConnection != null)
            {
                switch ((ConnectionStatus) e.Message)
                {
                    case ConnectionStatus.Connected:
                        if (dataService.isConnected)
                            StrategyPoolOnConnection(sender, e);
                        break;
                    case ConnectionStatus.Disconnected:
                        StrategyPoolOnConnection(sender, e);
                        break;
                    default:
                        break;
                }
            }
        }

        #endregion StrategyPool Call Backs

        #region IDisposable Members

        public void Dispose()
        {
            dataService.Dispose();
            orderService.Dispose();

            Dispose(true);
        }
        private void Dispose(bool disposing)
        {
            if (!disposed)
            {
                if (disposing)
                {
                    GC.SuppressFinalize(this);
                }

                disposed = true;
                dataService = null;
                orderService = null;
            }
        }

        #endregion
    }
}